﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using intelliEssay.Core;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Wordprocessing;
using intelliEssay.Interop;
using intelliEssay.Core.DocumentStructure;
using PPhelper = intelliEssay.Interop.ParagraphStyle;
using intelliEssay.Interop;
using intelliEssay.Interop.FontServices;
using System.Text.RegularExpressions;

namespace intelliEssay.Checkers.PaperLayoutAndParagraphAlignment
{
    public class PaperLayoutAndParagraphAlignmentChecker : IEssayChecker
    {
        public void Run(Task tsk)
        {
            CheckAlignment(tsk);
        }
        public void Cancel(Task tsk)
        {
            throw new NotImplementedException();
        }
        public void CheckAlignment(Task tsk)
        {
            bool CriticalTerminationFlag = false;
            try
            {
                SectionProperties sp = tsk.TaskObjectRoot.MainDocumentPart.Document.Body.LastChild as SectionProperties;
                MarginCheck(tsk, ref CriticalTerminationFlag, sp);
            }
            catch
            {

            }
            if (CriticalTerminationFlag)
            {
                return;
            }
            ParagraphAlignmentCheck(tsk);
        }

        private static void MarginCheck(Task tsk, ref bool CriticalTerminationFlag, SectionProperties sp)
        {
            if (sp != null)
            {
                foreach (OpenXmlElement oxe in sp.ChildElements)
                {
                    if (CriticalTerminationFlag)
                    {
                        return;
                    }
                    if (oxe.LocalName == "pgSz")
                    {
                        IList<OpenXmlAttribute> pgSzAttributes = oxe.GetAttributes();
                        foreach (OpenXmlAttribute pgSzAttribute in pgSzAttributes)
                        {
                            if (pgSzAttribute.LocalName == "w")
                            {
                                if (pgSzAttribute.Value != "11907" && pgSzAttribute.Value != "11906")
                                {
                                    CriticalTerminationFlag = true;
                                    tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(tsk) { Description = "纸张宽度设置错误，对齐检查无法继续" });
                                }
                            }
                            if (pgSzAttribute.LocalName == "h")
                            {
                                if (pgSzAttribute.Value != "16839" && pgSzAttribute.Value != "16838")
                                {
                                    CriticalTerminationFlag = true;
                                    tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(tsk) { Description = "纸张高度设置错误，对齐检查无法继续" });
                                }
                            }
                        }
                    }
                    if (oxe.LocalName == "pgMar")
                    {
                        IList<OpenXmlAttribute> pgMarAttributes = oxe.GetAttributes();
                        double GutterBounusMargin = 0;
                        double left = 0;
                        double right = 0;
                        foreach (OpenXmlAttribute pgMarAttribute in pgMarAttributes)
                        {
                            if (pgMarAttribute.LocalName == "gutter")
                            {
                                GutterBounusMargin = int.Parse(pgMarAttribute.Value) / 20 * UnitConversion.PointInMilimeter;
                            }
                            if (pgMarAttribute.LocalName == "right")
                            {
                                right = int.Parse(pgMarAttribute.Value) / 20 * UnitConversion.PointInMilimeter;
                            }
                            if (pgMarAttribute.LocalName == "left")
                            {
                                left = int.Parse(pgMarAttribute.Value) / 20 * UnitConversion.PointInMilimeter;
                            }
                        }
                        if (Math.Abs(left + GutterBounusMargin - 25.4) > 10)
                        {
                            CriticalTerminationFlag = true;
                            tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(tsk) { Description = "纸张左边距设置错误，对齐检查无法继续" });
                        }
                        if (Math.Abs(right + GutterBounusMargin - 25.4) > 10)
                        {
                            CriticalTerminationFlag = true;
                            tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(tsk) { Description = "纸张右边距设置错误，对齐检查无法继续" });
                        }
                        if (right - left != 0)
                        {
                            CriticalTerminationFlag = true;
                            tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(tsk) { Description = "纸张左右边距不一致，对齐检查无法继续" });
                        }
                    }
                }
            }
        }
        private static void ParagraphAlignmentCheck(Task tsk)
        {
            Justification just = new Justification();
            Paragraph _p = null;
            RunInfo ri = null;
            double FontSize = -1;
            double currentLInd = -1;
            double currentRInd = -1;
            var docPartsRemainders = Enum.GetValues(typeof(DocumentParts)).OfType<DocumentParts>().Where<DocumentParts>(dp =>
                {
                    return dp != DocumentParts.Menu && dp != DocumentParts.MenuContent && dp != DocumentParts.Appendix && dp != DocumentParts.AppendixContent && dp != DocumentParts.Cover && dp != DocumentParts.CoverInfo && dp != DocumentParts.AnnouncementContent && dp != DocumentParts.Announcement && dp != DocumentParts.LviParagraph
                        && dp != DocumentParts.KeywordInChineseContent && dp != DocumentParts.KeywordInEnglishContent && dp != DocumentParts.MainDocumentBodyInEnglish;
                });
            double DefaultFtsz = double.Parse(DocumentSpecificDefault.GetDefaultOrSettingByPropertyName<FontSize>(tsk.TaskObjectRoot, tsk.taskSetting.hostApp).Val);
            Justification standardJust;
            foreach (DocumentParts dp in docPartsRemainders)
            {
                List<Paragraph> pList = null;
                Paragraph standardFormat = StandardFormats.GetStandardFormatByDocumentPart(dp, 0, tsk);
                try
                {
                    pList = tsk.documentStructure.GetDocumentKeyElementList(dp, 0).Where<Paragraph>(o =>
                      {
                          return o.Parent != null && !o.InnerText.StartsWith("表") && !o.InnerText.StartsWith("图") && !o.InnerText.ToLowerInvariant().StartsWith("figure") && !o.InnerText.ToLowerInvariant().StartsWith("table") && Core.DocumentStructure.DocumentStructureHelper.IsParagraphEndingWithTitleInalidTrailingChars(o);
                      }).ToList<Paragraph>();
                }
                catch
                {
                    continue;
                }
                int count = pList.Count;


                //filter bullet list.


                //   pList.Add(tsk.documentStructure.AllParagraph[StartingParagraph.Value]);
                Justification currentJ = new Justification();
                standardJust = PPhelper.ParagraphPropertiesHelper.QParagraphPropertiesSingleValueProperty<Justification>(standardFormat, tsk.TaskObjectRoot, tsk.taskSetting.hostApp);
                currentLInd = -1;
                currentRInd = -1;
                foreach (Paragraph p in pList)
                {
                    if (p.InnerXml.Contains("<wps:") || (int)p.InnerText.Trim()[0] <= 256)
                    {
                        continue;
                    }
                    FontSize = DefaultFtsz;
                    try
                    {
                        RunInfoEx runInfoEx = new Interop.RunInfoEx(tsk.TaskObjectRoot, p.Elements<Run>().Count() == 0 ? p.Elements<Hyperlink>().FirstOrDefault<Hyperlink>().Elements<Run>().First() : p.Elements<Run>().First(), tsk.taskSetting.hostApp);
                        FontSize = double.Parse(runInfoEx["fontsize"]);
                    }
                    catch
                    {
                    }
                    Indentation indentation = PPhelper.ParagraphIndentation.GetHierachicalParagraphIndentationEx(p, tsk.TaskObjectRoot, tsk.taskSetting.hostApp);
                    Justification j = PPhelper.ParagraphPropertiesHelper.QParagraphPropertiesSingleValueProperty("Justification", p, tsk.TaskObjectRoot, tsk.taskSetting.hostApp);
                    if (currentJ.Val != null)
                    {
                        if (currentJ.Val.Value != j.Val.Value)
                        {
                            if (!((currentJ.Val == JustificationValues.Both && j.Val == JustificationValues.Left) || (currentJ.Val == JustificationValues.Left && j.Val == JustificationValues.Both)))
                            {
                                tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(ExceptionType.Alignment, "该段落对齐情况与同类型上一段落不统一", p, tsk, true));
                                currentJ = j;
                            }
                        }
                    }
                    double tempCurrLInd = DocumentAlignmentHelper.QParagraphEffectiveLeftIndentationInMilimeter(p, tsk.TaskObjectRoot, tsk.taskSetting.paperWidth, false, tsk.taskSetting.hostApp);
                    if (currentLInd != -1 && InThreshold(tsk.taskSetting.threshold, tempCurrLInd, currentLInd))
                    {
                        tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(ExceptionType.Alignment, "该段落左缩进情况与同类型上一段落不统一", p, tsk, true));
                        currentLInd = tempCurrLInd;
                    }
                    double tempCurrRInd = DocumentAlignmentHelper.QParagraphEffectiveRightIndentationInMilimeter(p, tsk.TaskObjectRoot, tsk.taskSetting.paperWidth);
                    if (currentRInd != -1 && InThreshold(tsk.taskSetting.threshold, tempCurrRInd, currentRInd))
                    {
                        tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(ExceptionType.Alignment, "该段落右缩进情况与同类型上一段落不统一", p, tsk, true));
                        currentRInd = tempCurrRInd;
                    }
                    if ((dp == DocumentParts.MainDocumentBody || dp.ToString().EndsWith("Content")) && dp != DocumentParts.ReferencedContent)
                    {
                        if (p.InnerText.Contains("启动Android模拟器之后"))
                        {
                        }
                        if (TestForLeftIndentationFirstLine(tsk, standardFormat, p, false, FontSize))
                        {
                            tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(ExceptionType.Alignment, dp.ToLocalizedString(0) + "首行应当缩进2字符", p, tsk, true));
                        }
                    }
                    if (standardJust.Val == JustificationValues.Center)
                    {
                        if (TestForCenterStatus(tsk, p))
                        {
                            tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(ExceptionType.Alignment, dp.ToLocalizedString(0) + "未居中", p, tsk, true));
                        }
                    }
                    if (currentJ.Val == null)
                    {
                        currentJ.Val = j.Val;
                    }
                    if (currentLInd == -1)
                    {
                        currentLInd = DocumentAlignmentHelper.QParagraphEffectiveLeftIndentationInMilimeter(p, tsk.TaskObjectRoot, tsk.taskSetting.paperWidth, false, tsk.taskSetting.hostApp);
                    }
                    if (currentRInd == -1)
                    {
                        currentRInd = DocumentAlignmentHelper.QParagraphEffectiveRightIndentationInMilimeter(p, tsk.TaskObjectRoot, tsk.taskSetting.paperWidth);
                    }
                }
            }


            LviCheck(tsk, tsk.documentStructure.LviParagraph, ref just, ref _p, ref ri, ref FontSize, ref currentLInd, ref currentRInd);
            LviCheck(tsk, tsk.documentStructure.BulletLists, ref just, ref _p, ref ri, ref FontSize, ref currentLInd, ref currentRInd);
        }

        private static void LviCheck(Task tsk, List<Paragraph>[] LviCollection, ref Justification just, ref Paragraph _p, ref RunInfo ri, ref double FontSize, ref double currentLInd, ref double currentRInd)
        {

            for (int i = 1; i < LviCollection.Length; i++)
            {

                just = new Justification();
                _p = StandardFormats.GetStandardFormatByDocumentPart(DocumentParts.LviParagraph, i, tsk);
                if (_p.ParagraphProperties.Justification == null)
                {
                    _p.ParagraphProperties.Justification = new Justification() { Val = JustificationValues.Left };
                }
                currentLInd = -1;
                currentRInd = -1;
                foreach (Paragraph p in LviCollection[i])
                {
                    if (p.InnerXml.Contains("<w:wsp"))
                    {
                        continue;
                    }
                    ri = new RunInfoHelper(tsk.TaskObjectRoot).GetRunInfo(p.ChildElements.First<Run>() == null ? p.ChildElements.First<Hyperlink>().ChildElements.First<Run>() : p.ChildElements.First<Run>());
                    FontSize = double.Parse(ri.FontSize) / 2 * UnitConversion.PointInMilimeter;
                    Justification j = PPhelper.ParagraphPropertiesHelper.QParagraphPropertiesSingleValueProperty("Justification", p, tsk.TaskObjectRoot, tsk.taskSetting.hostApp);
                    if (just.Val == null)
                    {
                        just = j;
                    }
                    if (just.Val.Value != j.Val.Value)
                    {
                        if (!((just.Val == JustificationValues.Both && j.Val == JustificationValues.Left) || (just.Val == JustificationValues.Left && j.Val == JustificationValues.Both)))
                        {
                            tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(ExceptionType.Alignment, "自适应::本段与本类段落的上一个对齐情况不一致", p, tsk, false));
                            just = j;
                        }
                    }
                    if (currentLInd == -1)
                    {
                        currentLInd = DocumentAlignmentHelper.QParagraphEffectiveLeftIndentationInMilimeter(p, tsk.TaskObjectRoot, tsk.taskSetting.paperWidth, false, tsk.taskSetting.hostApp);
                    }
                    if (currentRInd == -1)
                    {
                        currentRInd = DocumentAlignmentHelper.QParagraphEffectiveRightIndentationInMilimeter(p, tsk.TaskObjectRoot, tsk.taskSetting.paperWidth);
                    }
                    double tempCurrLInd = DocumentAlignmentHelper.QParagraphEffectiveLeftIndentationInMilimeter(p, tsk.TaskObjectRoot, tsk.taskSetting.paperWidth, false, tsk.taskSetting.hostApp);
                    if (InThreshold(tsk.taskSetting.threshold, tempCurrLInd, currentLInd))
                    {
                        tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(ExceptionType.Alignment, "该段落左缩进情况与同类型上一段落不统一", p, tsk, true));
                        currentLInd = tempCurrLInd;
                    }
                    double tempCurrRInd = DocumentAlignmentHelper.QParagraphEffectiveRightIndentationInMilimeter(p, tsk.TaskObjectRoot, tsk.taskSetting.paperWidth);
                    if (InThreshold(tsk.taskSetting.threshold, tempCurrRInd, currentRInd))
                    {
                        tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(ExceptionType.Alignment, "该段落右缩进情况与同类型上一段落不统一", p, tsk, true));
                        currentRInd = tempCurrRInd;
                    }
                    if (TestForRightIndentation(tsk, p))
                    {
                        tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(ExceptionType.Alignment, "段落右缩进设置错误，应当为0", p, tsk, true));
                    }
                    if (j.Val.Value != _p.ParagraphProperties.Justification.Val.Value)
                    {
                        if (j.Val.Value == JustificationValues.Both || _p.ParagraphProperties.Justification.Val.Value == JustificationValues.Left)
                            if (j.Val.Value == JustificationValues.Left || _p.ParagraphProperties.Justification.Val.Value == JustificationValues.Both)
                                tsk.AddEssayExceptionToEssayExceptionCollection(new EssayException(ExceptionType.Alignment, "段落对齐设置错误，应当为" + _p.ParagraphProperties.Justification.Val.Value.ToString(), p, tsk, true));
                    }
                }

            }
        }
        /// <summary>
        /// Something is wrong if returns true
        /// </summary>
        /// <param name="tsk"></param>
        /// <param name="p"></param>
        /// <returns></returns>
        private static bool TestForRightIndentation(Task tsk, Paragraph p)
        {
            return DocumentAlignmentHelper.QParagraphEffectiveRightIndentationInMilimeter(p, tsk.TaskObjectRoot, tsk.taskSetting.paperWidth) > tsk.taskSetting.threshold;
        }
        /// <summary>
        /// Something is wrong if returns true
        /// </summary>
        /// <param name="tsk"></param>
        /// <param name="_p">the standard paragraph for this type</param>
        /// <param name="p"></param>
        /// <param name="firstLine"></param>
        /// <param name="fontSize"></param>
        /// <returns></returns>
        private static bool TestForLeftIndentationFirstLine(Task tsk, Paragraph _p, Paragraph p, bool firstLine, double fontSize)
        {
            if (firstLine)
            {
                return Math.Abs(DocumentAlignmentHelper.QParagraphEffectiveLeftIndentationInMilimeter(p, tsk.TaskObjectRoot, tsk.taskSetting.paperWidth, false, tsk.taskSetting.hostApp) - DocumentAlignmentHelper.QParagraphEffectiveLeftIndentationInMilimeter(_p, tsk.TaskObjectRoot, tsk.taskSetting.paperWidth, false, tsk.taskSetting.hostApp)) > tsk.taskSetting.threshold;
            }
            else
            {
                fontSize = Interop.UnitConversion.PointInMilimeter * (fontSize / 2);
                double fl;
                double nfl;
                DocumentAlignmentHelper.QParagraphEffectiveLeftIndentationInMilimeterDualValue(p, tsk.TaskObjectRoot, out fl, out nfl, tsk.taskSetting.paperWidth, tsk.taskSetting.hostApp);

                return Math.Abs(Math.Abs(fl - nfl) - fontSize * 2) > tsk.taskSetting.threshold;
            }
        }
        /// <summary>
        /// something is wrong if returnes true
        /// </summary>
        private static bool TestForCenterStatus(Task tsk, Paragraph p)
        {
            return ParagraphCenterStatus.WellCentered != DocumentAlignmentHelper.QParagraphCenterStatusEx(p, tsk.TaskObjectRoot, tsk.taskSetting.threshold, tsk.taskSetting.hostApp);
        }
        /// <summary>
        /// Something is wrong if returned true
        /// </summary>
        /// <param name="threshold"></param>
        /// <param name="val1"></param>
        /// <param name="val2"></param>
        /// <returns></returns>
        private static bool InThreshold(double threshold, double val1, double val2)
        {
            return Math.Abs(val1 - val2) > threshold;
        }


        public string GetDescription(string cultureInfo)
        {
            return "Checks paper layouts, paper margins, parargaph indentations, paragraph spacing between lines, paragraph justifications and alignments.";
        }

        public string GetName(string cultureInfo)
        {
            return "Pargraph Alignment Checker";
        }
    }
}